package com.ramnight.sql2.ext;

import java.sql.Date;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ramnight.sql2.core.Sql2;
import com.ramnight.sql2.ext.name.INameConverter;

@SuppressWarnings("unchecked")
public class ExMap<M> {
	protected Map<String, Object> map;

	public M set(String key, Object value) {
		map.put(key, value);
		return (M) this;
	}
	
	public <T> T get(String key){
		return (T)map.get(key);
	}

	public Integer getInteger(String key) {
		return getInteger(key, null);
	}

	public Integer getInteger(String key, Integer def) {
		Object value = map.get(key);
		return value == null ? def : (Integer) value;
	}

	public String getString(String key) {
		return getString(key, null);
	}

	public String getString(String key, String def) {
		Object value = map.get(key);
		return value == null ? def : String.valueOf(value);
	}

	public Long getLong(String key) {
		return getLong(key, null);
	}

	public Long getLong(String key, Long def) {
		Object value = map.get(key);
		return value == null ? def : (Long) value;
	}
	
	public Boolean getBoolean(String key) {
		return getBoolean(key, null);
	}

	public Boolean getBoolean(String key, Boolean def) {
		Object value = map.get(key);
		return value == null ? def : (Boolean) value;
	}

	public Float getFloat(String key) {
		return getFloat(key, null);
	}

	public Float getFloat(String key, Float def) {
		Object value = map.get(key);
		return value == null ? def : (Float) value;
	}

	public Double getDouble(String key) {
		return getDouble(key, null);
	}

	public Double getDouble(String key, Double def) {
		Object value = map.get(key);
		return value == null ? def : (Double) value;
	}

	public Date getDate(String key) {
		return getDate(key, null);
	}

	public Date getDate(String key, Date def) {
		Object value = map.get(key);
		return value == null ? def : (Date) value;
	}

	public Timestamp getTimestamp(String key) {
		return getTimestamp(key, null);
	}

	public Timestamp getTimestamp(String key, Timestamp def) {
		Object value = map.get(key);
		return value == null ? def : (Timestamp) value;
	}

	public Map<String, Object> map() {
		return map;
	}

	public M map(Map<String, Object> map) {
		this.map = map;
		return (M) this;
	}

	public M map(JSONObject json) {
		for(Entry<String, Object> entry : json.entrySet()){
			if(entry.getValue() instanceof JSONObject
					|| entry.getValue() instanceof JSONArray){
				this.map.put(entry.getKey(), json.getString(entry.getKey()));
			}else{
				this.map.put(entry.getKey(), entry.getValue());
			}
		}
		return (M) this;
	}

	public JSONObject toJSONObject() {
		return new JSONObject(map);
	}

	public String toJSONString() {
		return new JSONObject(map).toJSONString();
	}

	public M retain(String... cols) {
		return filterMap(true, cols);
	}

	public M exclude(String... cols) {
		return filterMap(false, cols);
	}
	
	protected M filterMap(boolean isInclude, String... cols){
		Set<String> colSet = new HashSet<String>(Arrays.asList(cols));
		Iterator<Entry<String, Object>> it = map.entrySet().iterator();
		while (it.hasNext()) { //利用entrySet遍历删除时，会因为entrySet变化而导致遍历失败
			Entry<String, Object> entry = it.next();
			if(isInclude && !colSet.contains(entry.getKey())
				|| (!isInclude && colSet.contains(entry.getKey()))){
				it.remove();
			}
		}
		return (M) this;
	}

	public <T> T getPk() {
		return this.get(Sql2.getPk());
	}
	
	public boolean contains(String key){
		return this.map.containsKey(key);
	}
	
	public boolean isEmpty(){
		return this.map.isEmpty();
	}
	
	public Set<Entry<String, Object>> entrySet(){
		return this.map.entrySet();
	}
	
	public String concat(String...keys){
		StringBuilder sb = new StringBuilder();
		for(String key : keys){
			if(this.contains(key)){
				sb.append(this.getString(key));
			}
		}
		return sb.toString();
	}
	
	public M copy(ExMap<M> src, String...keys){
		for(String key : keys){
			this.set(key, src.get(key));
		}
		return (M)this;
	}
	
	public M nameConvert(INameConverter converter) {
		Map<String, Object> tempMap = new HashMap<>();
		for (Entry<String, Object> entry : map.entrySet()) {
			tempMap.put(converter.convert(entry.getKey()), entry.getValue());
		}
		this.map = tempMap;
		return (M) this;
	}
}
